#include "client_server_functions.h"
#include "array_functions.h"
#include <time.h>

void runFirstLevel(char *serverIp) {
	
	struct timeval	begin_send_time, end_send_time, begin_total_time, end_total_time;
	
	int *array, *arrayFinal;
	int arraySizeActual, arraySizeFinal;
	
	arraySizeActual = arraySize(1);
	arraySizeFinal = arraySize(4);
	
	connectToServer(&socketAsClient, serverIp, PORT);
	
	array = malloc(sizeof(int)*arraySizeActual);
	
	gettimeofday(&begin_total_time, NULL);
	createRandomArray(array, arraySizeActual, RANDOM_MIN, RANDOM_MAX);
	
	//printArray(array, arraySizeActual);

	gettimeofday(&begin_send_time, NULL);
    	send(socketAsClient,array,sizeof(int)*arraySizeActual,0);
	gettimeofday(&end_send_time, NULL); 	

	arrayFinal = malloc(sizeof(int)*arraySizeFinal);
	
	recv(socketAsClient,arrayFinal,sizeof(int)*arraySizeFinal,MSG_WAITALL);
	gettimeofday(&end_total_time, NULL);	
	    
    	printArray(arrayFinal, arraySizeFinal);
	
	double total_time = (double)(end_total_time.tv_sec *1000000 + end_total_time.tv_usec) 
                - ( begin_total_time.tv_sec*1000000 + begin_total_time.tv_usec); 
        double send_time = (double)(end_send_time.tv_sec *1000000 + end_send_time.tv_usec) 
                - ( begin_send_time.tv_sec*1000000 + begin_send_time.tv_usec); 
	
	printf("Time to Send Array[125] to Server = %2.3f us\n", send_time);
	printf("Total Sorting Time = %2.3f ms\n\n", total_time/1000);
}

void runSecondLevel(char *serverIp) {
	
	struct timeval begin_send_to_1_time, end_send_to_1_time, begin_recv_from_1_time, end_recv_from_1_time;
	struct timeval begin_send_to_3_time, end_send_to_3_time, begin_recv_from_3_time, end_recv_from_3_time;
	struct timeval begin_sort_time, end_sort_time;

	int *array, *arrayFinal;
	int arraySizeActual, arraySizeFinal, arraySizeClient;
	
	arraySizeActual = arraySize(2);
	arraySizeClient = arraySize(1);
	arraySizeFinal = arraySize(4);

	createServer(&socketAsServer, PORT);
	acceptClients(socketAsServer, socketToClient, 2);
	
	array = malloc(sizeof(int)*arraySizeActual);
	
	gettimeofday(&begin_recv_from_1_time, NULL);
	recv(socketToClient[0],array,sizeof(int)*arraySizeClient,MSG_WAITALL);
	recv(socketToClient[1],array+arraySizeClient,sizeof(int)*arraySizeClient,MSG_WAITALL);
	gettimeofday(&end_recv_from_1_time, NULL);
	
	gettimeofday(&begin_sort_time, NULL);
	qsort(array, arraySizeActual, sizeof(int), compareFunction);
	gettimeofday(&end_sort_time, NULL);

	//printArray(array, arraySizeActual);
	
	connectToServer(&socketAsClient, serverIp, PORT);
	
	gettimeofday(&begin_send_to_3_time, NULL);
    	send(socketAsClient,array,sizeof(int)*arraySizeActual,0);
	gettimeofday(&end_send_to_3_time, NULL);	
    
	arrayFinal = malloc(sizeof(int)*arraySizeFinal);
	
	gettimeofday(&begin_recv_from_3_time, NULL);
	recv(socketAsClient,arrayFinal,sizeof(int)*arraySizeFinal,MSG_WAITALL);
	gettimeofday(&end_recv_from_3_time, NULL);	
	
	gettimeofday(&begin_send_to_1_time, NULL);
    	send(socketToClient[0],arrayFinal,sizeof(int)*arraySizeFinal,0);
    	send(socketToClient[1],arrayFinal,sizeof(int)*arraySizeFinal,0);
	gettimeofday(&end_send_to_1_time, NULL);
	
	double recv_from_1_time = (double)(end_recv_from_1_time.tv_sec *1000000 + end_recv_from_1_time.tv_usec) 
                - ( begin_recv_from_1_time.tv_sec*1000000 + begin_recv_from_1_time.tv_usec); 
        double sort_time = (double)(end_sort_time.tv_sec *1000000 + end_sort_time.tv_usec) 
                - ( begin_sort_time.tv_sec*1000000 + begin_sort_time.tv_usec); 
	double send_to_3_time = (double)(end_send_to_3_time.tv_sec *1000000 + end_send_to_3_time.tv_usec) 
                - ( begin_send_to_3_time.tv_sec*1000000 + begin_send_to_3_time.tv_usec); 
        double recv_from_3_time = (double)(end_recv_from_3_time.tv_sec *1000000 + end_recv_from_3_time.tv_usec) 
                - ( begin_recv_from_3_time.tv_sec*1000000 + begin_recv_from_3_time.tv_usec);	
	double send_to_1_time = (double)(end_send_to_1_time.tv_sec *1000000 + end_send_to_1_time.tv_usec) 
                - ( begin_send_to_1_time.tv_sec*1000000 + begin_send_to_1_time.tv_usec);

	printf("\nTime to Receive Array[125] from Clients = %2.3f us\n", recv_from_1_time);
	printf("Local Sorting Time = %2.3f us\n", sort_time);
	printf("Time to Send Array[250] to Server = %2.3f us\n", send_to_3_time);
	printf("Time to Receive Array[1000] from Server = %2.3f us\n", recv_from_3_time);
	printf("Time to Send Array[1000] to Clients = %2.3f us\n\n", send_to_1_time);
}

void runThirdLevel(char *serverIp) {
		
	struct timeval begin_send_to_2_time, end_send_to_2_time, begin_recv_from_2_time, end_recv_from_2_time;
	struct timeval begin_send_to_4_time, end_send_to_4_time, begin_recv_from_4_time, end_recv_from_4_time;
	struct timeval begin_sort_time, end_sort_time;	
		
	int *array, *arrayFinal;
	int arraySizeActual, arraySizeFinal, arraySizeClient;
	
	arraySizeActual = arraySize(3);
	arraySizeClient = arraySize(2);
	arraySizeFinal = arraySize(4);

	createServer(&socketAsServer, PORT);
	acceptClients(socketAsServer, socketToClient, 2);
	
	array = malloc(sizeof(int)*arraySizeActual);
	
	gettimeofday(&begin_recv_from_2_time, NULL);
	recv(socketToClient[0],array,sizeof(int)*arraySizeClient,MSG_WAITALL);
	recv(socketToClient[1],array+arraySizeClient,sizeof(int)*arraySizeClient,MSG_WAITALL);
	gettimeofday(&end_recv_from_2_time, NULL);	
	
	gettimeofday(&begin_sort_time, NULL);
	qsort(array, arraySizeActual, sizeof(int), compareFunction);
	gettimeofday(&end_sort_time, NULL);
	
	//printArray(array, arraySizeActual);
	
	connectToServer(&socketAsClient, serverIp, PORT);
	
	gettimeofday(&begin_send_to_4_time, NULL);
    	send(socketAsClient,array,sizeof(int)*arraySizeActual,0);
	gettimeofday(&end_send_to_4_time, NULL);    	

	arrayFinal = malloc(sizeof(int)*arraySizeFinal);
	
	gettimeofday(&begin_recv_from_4_time, NULL);
	recv(socketAsClient,arrayFinal,sizeof(int)*arraySizeFinal,MSG_WAITALL);
	gettimeofday(&end_recv_from_4_time, NULL);	

	gettimeofday(&begin_send_to_2_time, NULL);
    	send(socketToClient[0],arrayFinal,sizeof(int)*arraySizeFinal,0);
    	send(socketToClient[1],arrayFinal,sizeof(int)*arraySizeFinal,0);
	gettimeofday(&end_send_to_2_time, NULL);	

    	//printArray(arrayFinal, arraySizeFinal);
	
	double recv_from_2_time = (double)(end_recv_from_2_time.tv_sec *1000000 + end_recv_from_2_time.tv_usec) 
                - ( begin_recv_from_2_time.tv_sec*1000000 + begin_recv_from_2_time.tv_usec); 
        double sort_time = (double)(end_sort_time.tv_sec *1000000 + end_sort_time.tv_usec) 
                - ( begin_sort_time.tv_sec*1000000 + begin_sort_time.tv_usec); 
	double send_to_4_time = (double)(end_send_to_4_time.tv_sec *1000000 + end_send_to_4_time.tv_usec) 
                - ( begin_send_to_4_time.tv_sec*1000000 + begin_send_to_4_time.tv_usec); 
        double recv_from_4_time = (double)(end_recv_from_4_time.tv_sec *1000000 + end_recv_from_4_time.tv_usec) 
                - ( begin_recv_from_4_time.tv_sec*1000000 + begin_recv_from_4_time.tv_usec);	
	double send_to_2_time = (double)(end_send_to_2_time.tv_sec *1000000 + end_send_to_2_time.tv_usec) 
                - ( begin_send_to_2_time.tv_sec*1000000 + begin_send_to_2_time.tv_usec);

	printf("\nTime to Receive Array[250] from Clients = %2.3f us\n", recv_from_2_time);
	printf("Local Sorting Time = %2.3f us\n", sort_time);
	printf("Time to Send Array[500] to Server = %2.3f us\n", send_to_4_time);
	printf("Time to Receive Array[1000] from Server = %2.3f us\n", recv_from_4_time);
	printf("Time to Send Array[1000] to Clients = %2.3f us\n\n", send_to_2_time);
}

void runFourthLevel() {
	
	struct timeval	begin_send_time, end_send_time, begin_recv_time, end_recv_time, begin_sort_time, end_sort_time;	
	
	int *array;
	int arraySizeActual, arraySizeClient;
	
	arraySizeActual = arraySize(4);
	arraySizeClient = arraySize(3);

	createServer(&socketAsServer, PORT);
	acceptClients(socketAsServer, socketToClient, 2);
	
	array = malloc(sizeof(int)*arraySizeActual);
	
	gettimeofday(&begin_recv_time, NULL);
	recv(socketToClient[0],array,sizeof(int)*arraySizeClient,MSG_WAITALL);
	recv(socketToClient[1],array+arraySizeClient,sizeof(int)*arraySizeClient,MSG_WAITALL);
	gettimeofday(&end_recv_time, NULL);	
	
	gettimeofday(&begin_sort_time, NULL);
	qsort(array, arraySizeActual, sizeof(int), compareFunction);
	gettimeofday(&end_sort_time, NULL);	
	
	//printArray(array, arraySizeActual);
	
	gettimeofday(&begin_send_time, NULL);
    	send(socketToClient[0],array,sizeof(int)*arraySizeActual,0);
    	send(socketToClient[1],array,sizeof(int)*arraySizeActual,0);
	gettimeofday(&end_send_time, NULL);
    
	//printArray(array, arraySizeActual);

	double recv_time = (double)(end_recv_time.tv_sec *1000000 + end_recv_time.tv_usec) 
                - ( begin_recv_time.tv_sec*1000000 + begin_recv_time.tv_usec);
	double sort_time = (double)(end_sort_time.tv_sec *1000000 + end_sort_time.tv_usec) 
                - ( begin_sort_time.tv_sec*1000000 + begin_sort_time.tv_usec); 
        double send_time = (double)(end_send_time.tv_sec *1000000 + end_send_time.tv_usec) 
                - ( begin_send_time.tv_sec*1000000 + begin_send_time.tv_usec); 
	
	printf("\nTime to Receive Array[500] from Clients = %2.3f us\n", recv_time);
	printf("Local Sorting Time = %2.3f us\n", sort_time);
	printf("Time to Send Array[1000] to Clients = %2.3f us\n\n", send_time);
	
}

int main(int argc, char** argv) {
	
	char serverIp[20];
	time_t t;
	
	/* Intializes random number generator */
	srand((unsigned) time(&t));
	
	if(argc == 1) {
		quit(USAGE, EXIT_FAILURE);
	}else if(argc == 2) {
		if(atoi(argv[1]) != 4) {
			quit(USAGE, EXIT_FAILURE);
		}
	}else if(argc == 3) {
		if(atoi(argv[1]) == 1 || atoi(argv[1]) == 2 || atoi(argv[1]) == 3) {
			strcpy(serverIp, argv[2]);
		} else {
			quit(USAGE, EXIT_FAILURE);
		}
	}else {
		quit(USAGE, EXIT_FAILURE);	
	}

	switch(atoi(argv[1])) {
		case 1:
			runFirstLevel(serverIp);
			break;
		case 2:
			runSecondLevel(serverIp);
			break;
		case 3:
			runThirdLevel(serverIp);
			break;
		case 4:
			runFourthLevel();
			break;
		default:
			quit(USAGE, EXIT_FAILURE);
	}

	return (EXIT_SUCCESS);
}
